<?php
namespace Sfsn\Ext;

/**
 * 抽奖概率类
 * @author shooke
 *         
 *         $prize_arr = [
 *         '0' => ['id' => 1, 'prize' => '平板电脑', 'v' => 1],
 *         '1' => ['id' => 2,'prize' => '数码相机','v' => 5],
 *         '2' => ['id' => 3,'prize' => '音箱设备','v' => 10],
 *         '3' => ['id' => 4,'prize' => '4G优盘','v' => 12],
 *         '4' => ['id' => 5,'prize' => '10Q币','v' => 22],
 *         '5' => ['id' => 6,'prize' => '下次没准就能中哦','v' => 50]
 *         ];
 *         //初始化抽奖数组和概率参数
 *         $a = new chance($prize_arr,'v');
 *         print_r($a->getRand());//抽奖，默认是返回中奖信息的，但是可以通过getYes再次获取中奖结果
 *         print_r($a->getYes());//获取中奖结果，为了多次查看同一次抽奖的结果
 *         print_r($a->getNo());//获取未中奖结果
 *        
 */
class Chance
{

    /**
     * 中奖数组
     */
    public $yes = '';

    /**
     * 奖品数组
     */
    public $data = [];

    /**
     * 抽奖算法数组
     */
    public $draw = [];

    /**
     * 奖项数组
     * 是一个二维数组，记录了所有本次抽奖的奖项信息，
     * 其中表示data奖项，prize表示奖品，chance表示中奖概率。
     * 注意其中的v必须为整数，你可以将对应的 奖项的v设置成0，即意味着该奖项抽中的几率是0，
     * 数组中v的总和（基数），基数越大越能体现概率的准确性。
     * 本例中v的总和为100，那么平板电脑对应的 中奖概率就是1%，
     * 如果v的总和是10000，那中奖概率就是万分之一了。
     * $data = [
     * '0' => ['prize'=>'平板电脑','chance'=>1],
     * '1' => ['prize'=>'数码相机','chance'=>5],
     * '2' => ['prize'=>'音箱设备','chance'=>10],
     * '3' => ['prize'=>'4G优盘','chance'=>12],
     * '4' => ['prize'=>'10Q币','chance'=>22],
     * '5' => ['prize'=>'下次没准就能中哦','v'=>50],
     * ];
     */
    public function __construct($data, $chance = 'v')
    {
        $this->data = $data;
        $this->format($chance);
    }

    /**
     * 处理得到概率数组 [数据key=>中奖概率]
     * $data = [
     * '0' => ['prize'=>'平板电脑','chance'=>1],
     * '1' => ['prize'=>'数码相机','chance'=>5],
     * '2' => ['prize'=>'音箱设备','chance'=>10],
     * '3' => ['prize'=>'4G优盘','chance'=>12],
     * '4' => ['prize'=>'10Q币','chance'=>22],
     * '5' => ['prize'=>'下次没准就能中哦','v'=>50],
     * ];
     * 得到
     * $draw = [
     * '0' => 1,
     * '1' => 5,
     * '2' => 10,
     * '3' => 12,
     * '4' => 22,
     * '5' => 50,
     * ];
     */
    public function format($chance)
    {
        // 原始数据
        $data = $this->data;
        foreach ($data as $key => $val) {
            // 抽奖概率数组
            $this->draw[$key] = $val[$chance];
        }
        // 返回抽奖概率数组
        return $this->draw;
    }

    /**
     * 经典的概率算法，
     * $proArr是一个预先设置的数组，
     * 假设数组为：array(100,200,300，400)，
     * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内，
     * 如果不在，则将概率空间，也就是k的值减去刚刚的那个数字的概率空间，
     * 在本例当中就是减去100，也就是说第二个数是在1，900这个范围内筛选的。
     * 这样 筛选到最终，总会有一个数满足要求。
     * 就相当于去一个箱子里摸东西，
     * 第一个不是，第二个不是，第三个还不是，那最后一个一定是。
     * 这个算法简单，而且效率非常 高，
     * 关键是这个算法已在我们以前的项目中有应用，尤其是大数据量的项目中效率非常棒。
     */
    public function getRand()
    {
        $draw = $this->draw;
        // 概率数组的总概率精度
        $proSum = array_sum($draw);
        // 概率数组循环
        foreach ($draw as $key => $val) {
            $rand = mt_rand(1, $proSum);
            if ($rand <= $val) {
                // 中奖记录并退出循环
                $this->yes = $key;
                break;
            } else {
                $proSum -= $val;
            }
        }
        // 返回中奖信息
        return $this->getYes();
    }

    /**
     * 获取中奖数据
     */
    public function getYes()
    {
        // 中奖记录
        $key = $this->yes;
        // 返回中奖结果
        return $this->data[$key];
    }

    /**
     * 获取未中奖数据
     */
    public function getNo()
    {
        // 中奖记录
        $key = $this->yes;
        // 原始数据
        $no = $this->data;
        // 删除中奖项
        unset($no[$key]);
        // 打乱顺序
        shuffle($no);
        // 返回未中奖结果
        return $no;
    }
}
